﻿
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System;
using System.Text;
using System.Windows.Forms;
using StableDiffusionTools.Interop;

namespace StableDiffusionTools.Utils
{
    public static class ImageUtil
    {
        //"JPEG(*.jpg)|*.jpg;*.jpeg|PNG(*.png)|*.png|GIF(*.gif)|*.gif|TIF(*.tif)|*.tif;*.tiff|Bitmap(*.bmp)|*.bmp";
        //"All files (*.*)|*.*"
        // = "Image Files(*.bmp;*.jpg;*.jpeg;*.gif;*.tif;*.tiff;*.png)|*.bmp;*.jpg;*.jpeg;*.gif;*.tif;*.tiff;*.png";       
        public static readonly string CommonImageFilter = "";
        public static readonly List<string> CommonImageExtensions = new List<string>();

        public static readonly List<Tuple<string, string[]>> ImageExtensions;
        static ImageUtil()
        {
            ImageExtensions = new List<Tuple<string, string[]>>
            {
                new Tuple<string, string[]>("JPEG", new string[] { ".jpe", ".jpg", ".jpeg"}),
                new Tuple<string, string[]>("Bitmap", new string[] { ".bmp", ".dib" }),
                new Tuple<string, string[]>("PNG", new string[] { ".png" }),
                new Tuple<string, string[]>("GIF", new string[] { ".gif" }),
                new Tuple<string, string[]>("TIFF", new string[] { ".tif", ".tiff" }),
                new Tuple<string, string[]>("TGA", new string[] { ".tga" }),
                //new Tuple<string, string[]>("WEBP", new string[] { ".webp" })
            };

            for (int i = 0; i < ImageExtensions.Count; i++)
            {
                for (int j = 0; j < ImageExtensions[i].Item2.Length; j++)
                {
                    string ext = ImageExtensions[i].Item2[j];
                    CommonImageExtensions.Add(ext);
                }
            }

            CommonImageFilter = BuildFilter("Image Files", CommonImageExtensions);
            for (int i = 0; i < ImageExtensions.Count; i++)
            {
                CommonImageFilter = string.Format("{0}|{1}", CommonImageFilter, BuildFilter(ImageExtensions[i]));
            }
        }

        private static string BuildFilter(Tuple<string, string[]> tuple) => BuildFilter(tuple.Item1, tuple.Item2);
        private static string BuildFilter(string name, IEnumerable<string> extensions)
        {
            StringBuilder sb = new StringBuilder();
            bool first = true;
            foreach (var ext in extensions)
            {
                if (first)
                {
                    first = false;
                    sb.AppendFormat("*{0}", ext);             
                }
                else
                {
                    sb.AppendFormat(";*{0}", ext);
                }
            }
            return string.Format("{0}({1})|{1}", name, sb.ToString());
        }

        public static bool MatchesImageExtension(string extension)
        {
            if (string.IsNullOrEmpty(extension))
                return false;

            return CommonImageExtensions.Contains(extension.ToLower());
        }

        public static bool EndsWithImageExtension(string path, StringComparison comparison = StringComparison.OrdinalIgnoreCase)
        {
            if (string.IsNullOrEmpty(path))
                return false;

            bool result = false;
            foreach (var item in CommonImageExtensions)
            {
                if (path.EndsWith(item, comparison))
                {
                    result = true;
                    break;
                }
            }

            return result;
        }

        public static bool EndsWithWebpExtension(string path, StringComparison comparison = StringComparison.OrdinalIgnoreCase)
        {
            if (string.IsNullOrEmpty(path))
                return false;

            bool result = false;

            if (path.EndsWith(".webp", comparison))
            {
                result = true;
            }

            return result;
        }

        public static float CalculateAspectRatio(Size source, Size destination)
        {
            float ratioX = (float)destination.Width / source.Width;
            float ratioY = (float)destination.Height / source.Height;
            float ratio = ratioX < ratioY ? ratioX : ratioY;
            return ratio;
        }

        public static string ColorToHexRGB(Color c)
        {
            return string.Format("#{0:X2}{1:X2}{2:X2}", c.R, c.G, c.B);
        }

        public static string ColorToHexARGB(Color c)
        {
            return string.Format("#{0:X2}{1:X2}{2:X2}{3:X2}", c.A, c.R, c.G, c.B);
        }

        public static Bitmap LoadBitmap(string filePath, PixelFormat pixelFormat = PixelFormat.Format32bppPArgb, bool useICM = true)
        {
            Bitmap Result = null;
            try
            {
                using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
                {
                    using (var tmpBitmap = new Bitmap(stream, useICM))
                    {
                        //Result = tmpBitmap.Clone(new Rectangle(0, 0, tmpBitmap.Width, tmpBitmap.Height), pixelFormat);
                        Result = CopyBitmap(tmpBitmap);
                    }
                }
            }
            catch (ArgumentException) { } //invalid stream passed in
            catch (FileNotFoundException) { } //image removed, outdated cache?

            return Result;
        }

        public static Bitmap CopyBitmap(Bitmap bitmap, PixelFormat pixelFormat = PixelFormat.Format32bppPArgb)
        {
            Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
            Bitmap bitmapDest = new Bitmap(bitmap.Width, bitmap.Height, pixelFormat);
            BitmapData data = bitmap.LockBits(rect, ImageLockMode.ReadOnly, pixelFormat);
            BitmapData dataDest = bitmapDest.LockBits(rect, ImageLockMode.WriteOnly, pixelFormat);
            Kernel32.CopyMemory(dataDest.Scan0, data.Scan0, (uint)data.Stride * (uint)data.Height);
            bitmap.UnlockBits(data);
            bitmapDest.UnlockBits(dataDest);
            return bitmapDest;
        }

        public static Image LoadImage(string filePath, bool useICM = true)
        {
            Image Result = null;
            try
            {
                Result = Image.FromFile(filePath, useICM);
            }
            catch (ArgumentException) { }

            return Result;
        }

        public static Icon BitmapToIcon(Bitmap bitmap)
        {
            IntPtr Hicon = bitmap.GetHicon();
            Icon myIcon = null;
            using (var tempIcon = Icon.FromHandle(Hicon))
            {
                myIcon = (Icon)tempIcon.Clone();
            }
            User32.DestroyIcon(Hicon);
            return myIcon;
        }

        public static IntPtr CreateCompatibleDC(Control control)
        {
            IntPtr result;
            using (Graphics g = control.CreateGraphics())
                result = Gdi32.CreateCompatibleDC(g);

            return result;
        }

        /// <summary>
        /// Save image as Jpeg with options for compression.
        /// </summary>
        /// <param name="image">Image to save.</param>
        /// <param name="savePath">Path to save to</param>
        /// <param name="quality">100 = best quality 0 = best compression</param>
        public static void SaveJPEG(Image image, string savePath, int quality)
        {

            ImageCodecInfo codec = GetCodecFromImageFormat(ImageFormat.Jpeg);
            if (codec != null)
            {
                image.Save(savePath, codec, GetEncoderParameters(quality));
            }
            else
            {
                image.Save(savePath, ImageFormat.Jpeg);
            }
        }


        public static void SavePNG(Image image, string savePath, int quality)
        {

            ImageCodecInfo codec = GetCodecFromImageFormat(ImageFormat.Png);
            if (codec != null)
            {
                image.Save(savePath, codec, GetEncoderParameters(quality));
            }
            else
            {
                image.Save(savePath, ImageFormat.Png);
            }
        }


        public static void SaveImage(Image image, string savePath, ImageFormat toFormat)
        {
            if (toFormat == ImageFormat.Jpeg)
            {
                ImageCodecInfo codec = GetCodecFromImageFormat(toFormat);
                if (codec != null)
                    image.Save(savePath + ".jpg", codec, GetEncoderParameters(95));
                else
                    image.Save(savePath + ".jpg", ImageFormat.Jpeg);
            }
            else if (toFormat == ImageFormat.Png)
                image.Save(savePath + ".png", ImageFormat.Png);
            else
                throw new InvalidOperationException("not supported");
        }

        public static ImageCodecInfo GetCodecFromImageFormat(ImageFormat format)
        {
            return GetCodecFromGUID(format.Guid);
        }

        public static ImageCodecInfo GetCodecFromImage(Image i)
        {
            return GetCodecFromGUID(i.RawFormat.Guid);
        }

        public static ImageCodecInfo GetCodecFromGUID(Guid guid)
        {
            foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageDecoders())
                if (codec.FormatID == guid)
                    return codec;

            return null;
        }

        public static ImageCodecInfo GetCodecFromMimeType(string mimeType)
        {
            foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageDecoders())
                if (codec.MimeType == mimeType)
                    return codec;

            return null;
        }

        public static EncoderParameters GetEncoderParameters(int quality)
        {
            EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
            EncoderParameters encoderParams = new EncoderParameters(1);
            encoderParams.Param[0] = qualityParam;
            return encoderParams;
        }

        public static ImageFormat GetImageFormatFromExtension(string ext)
        {
            ext = ext.Trim('.').ToLower();
            switch (ext)
            {
                case "png":
                    return ImageFormat.Png;
                case "jpe":
                case "jpg":
                case "jpeg":
                    return ImageFormat.Jpeg;
                case "tif":
                case "tiff":
                    return ImageFormat.Tiff;
                case "bmp":
                case "dib":
                    return ImageFormat.Bmp;
                case "gif":
                    return ImageFormat.Gif;
                default:
                    return null;
            }
        }
    }
}
